recording-daemon: lifecycle notification events#2142
Conversation
a4ccba2 to
fd097da
Compare
rfuchs
left a comment
There was a problem hiding this comment.
This is all AI generated, isn't it
Good eye 🙂 |
a6f6824 to
bf74310
Compare
Extend rtpengine-recording HTTP/command notifications beyond the historical finished-only path so operators can track call and stream recording lifecycle. Events (notify-events CSV, default: finished): opened, started, finished, discarded, failed, call-started, call-finished, call-discarded New options: notify-events, notify-json, notify-no-metadata, notify-command-format, notify-queue-limit. Docs/conf only document the new notify options; upstream storage (S3/GCS/output-storage) docs are left unchanged. Backward compatible: default mask is finished only. Includes unit tests and Docker e2e smoke harness.
notify_setup must not require notify-uri/notify-command: the same thread pool backs S3, GCS and DB notif_action_t work. Also leave object_name to action cleanup to avoid double-free on those paths.
70d8ba9 to
a1ae952
Compare
rfuchs
left a comment
There was a problem hiding this comment.
Not sure if I'm willing to accept this as it all looks like an overly complicated wall of code, typical of AI slop
| static void notify_req_snapshot(notif_req_t *req, enum notify_event event, | ||
| output_t *o, metafile_t *mf, tag_t *tag, | ||
| const char *error_code, const char *error_message) | ||
| { | ||
| req->event = event; | ||
| req->terminal = notify_event_is_terminal(event) ? 1 : 0; | ||
|
|
||
| if (mf) { | ||
| req->call_id = g_strdup(mf->call_id); | ||
| req->call_start = (double) mf->start_time_us / 1000000.; | ||
| req->call_db_id = mf->db_id; | ||
| if (!notify_no_metadata && mf->metadata) | ||
| req->metadata = g_strdup(mf->metadata); | ||
| } |
There was a problem hiding this comment.
This whole function seems redundant? It copes all the fields into a notif_req_t, only to have the fields copied again in notify_enqueue() and then freed?
| const char *call_id = req->call_id ? req->call_id : (mf && mf->call_id ? mf->call_id : NULL); | ||
| const char *file_name = req->file_name ? req->file_name : (o && o->file_name ? o->file_name : NULL); | ||
| const char *file_format = req->file_format ? req->file_format : (o && o->file_format ? o->file_format : NULL); | ||
| const char *kind = req->kind ? req->kind : (o && o->kind ? o->kind : NULL); | ||
| const char *full_filename = req->full_filename ? req->full_filename : | ||
| (o && o->full_filename ? o->full_filename : NULL); | ||
| const char *output_id = req->output_id ? req->output_id : (o && o->output_id ? o->output_id : NULL); | ||
| const char *metadata = req->metadata ? req->metadata : | ||
| (!notify_no_metadata && mf && mf->metadata ? mf->metadata : NULL); | ||
| double call_start = req->call_start > 0 ? req->call_start : | ||
| (mf ? (double) mf->start_time_us / 1000000. : 0); | ||
| double stream_start = req->stream_start > 0 ? req->stream_start : | ||
| (o ? (double) o->start_time_us / 1000000. : call_start); | ||
| unsigned long long call_db_id = req->call_db_id ? req->call_db_id : (mf ? mf->db_id : 0); |
There was a problem hiding this comment.
The call ID and the other fields are copied from the metafile_t or the output anyway. What's the reason for having all these case distinctions here?
| notif_req_t *first = rtpe_g_tree_first(notify_timers); | ||
| if (!first) | ||
| break; | ||
| g_tree_remove(notify_timers, first); | ||
| nont_inflight_dec(first); | ||
| if (first->action && first->action->cleanup) | ||
| first->action->cleanup(first); | ||
| req_free_fields(first); | ||
| g_free(first->name); | ||
| g_free(first); | ||
| } | ||
| g_tree_destroy(notify_timers); | ||
| notify_timers = NULL; |
There was a problem hiding this comment.
This seems like a candidate for g_tree_foreach ?
Summary
Extends
rtpengine-recordingnotifications from the historical finished-only path to a configurable call/stream lifecycle event set, while remaining backward compatible.Events (
notify-eventsCSV)openedrecording_file_openedstartedrecording_startedfinished(default)recording_finisheddiscardedrecording_discardedfailedrecording_failedcall-startedcall_recording_startedcall-finishedcall_recording_finishedcall-discardedcall_recording_discardedallDefault remains
finishedonly so existing deployments are unchanged.New options
notify-events— CSV list (defaultfinished)notify-json— POST JSON body via json-glibnotify-no-metadata— omit call metadata from payloadsnotify-command-format—legacy|extended|json-envnotify-queue-limit— bound non-terminal queue depth (default 1000;0= unlimited). Terminal events are always accepted.Design
notif_action_t/notif_req_t) andlib/http.chelpersnotify_events.c(shared with unit tests)output.c; call hooks inmetafile.c/packet.cnotif_reqbefore queueing (workers never touch live objects)Tests
t/test-notify-events.c— PASSt/notify_e2e/temp Python HTTP receiver + metafile lifecycle — E2E_NOTIFY_OKcall_recording_finishedwith JSON body + lifecycle headersDocs
docs/rtpengine-recording.mdetc/rtpengine-recording.confExample
Compatibility
notify-commandargv (path,db_id) preserved whennotify-command-format=legacyI used an AI assistant to help with some of the repetitive work (e.g. scaffolding, draft text), but the architecture, behaviour, and all final code changes were reviewed, adapted, and validated by me. The goal is to speed up the mechanical parts while still having a human developer take responsibility for correctness, style, and alignment with rtpengine 's conventions. Feedback is very welcome and I'll iterate on the implementation as needed.